﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace AwesomeEnginePC.Physics
{
    [Serializable]
    public class Vector<T> where T : IComparable<T>, IEquatable<T>
    {
        /// <summary>
        /// Creates a new Vector object with default values.
        /// </summary>
        public Vector()
        {
            X = default(T);
            Y = default(T);
        }

        /// <summary>
        /// Creates a new Vector object with custom values.
        /// </summary>
        /// <param name="x">The x component of the vector.</param>
        /// <param name="y">The y component of the vector.</param>
        public Vector(T x, T y)
        {
            X = x;
            Y = y;
        }

        /// <summary>
        /// The length of the vector.
        /// </summary>
        public double Length { get { return Math.Sqrt(XValue * XValue + YValue * YValue); } }

        /// <summary>
        /// A vector representing the normal of the current vector.
        /// </summary>
        public Vector<T> Normal { get { return new Vector<T>(-YValue / Length, XValue / Length); } }

        /// <summary>
        /// The x component of the vector.
        /// </summary>
        public T X { get; set; }

        /// <summary>
        /// The y component of the vector.
        /// </summary>
        public T Y { get; set; }

        private dynamic XValue { get { return X; } }
        private dynamic YValue { get { return Y; } }
        public static Vector<T> operator -(Vector<T> vector, T num)
        {
            return new Vector<T>(vector.XValue - num, vector.YValue - num);
        }

        public static bool operator !=(Vector<T> left, Vector<T> right)
        {
            return !(left == right);
        }

        public static Vector<T> operator *(Vector<T> vector, T num)
        {
            return new Vector<T>(vector.XValue * num, vector.YValue * num);
        }

        public static Vector<T> operator /(Vector<T> vector, T num)
        {
            return new Vector<T>(vector.XValue / num, vector.YValue / num);
        }

        public static Vector<T> operator +(Vector<T> left, Vector<T> right)
        {
            return new Vector<T>(left.XValue + right.XValue, left.YValue + right.YValue);
        }

        public static Vector<T> operator +(Vector<T> vector, T num)
        {
            return new Vector<T>(vector.XValue + num, vector.YValue + num);
        }

        public static bool operator ==(Vector<T> left, Vector<T> right)
        {
            if (object.ReferenceEquals(left, right))
                return true;

            if ((object)left == null || (object)right == null)
                return false;

            return (left.XValue == right.XValue) && (left.YValue == right.YValue);
        }

        /// <summary>
        /// Performs a dot product on the current vector and the inputted vector.
        /// </summary>
        /// <param name="v">Another vector to perform the operation with.</param>
        /// <example>With two vectors {3,4} and {2,5}, the dot product would be (3*2) + (4*5) = 26.</example>
        /// <returns></returns>
        public double DotProduct(Vector<T> v)
        {
            return XValue * v.XValue + YValue * v.YValue;
        }

        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        /// <summary>
        /// Returns a Vector that is inverted from the current Vector. Does not change the current Vector.
        /// </summary>
        /// <returns></returns>
        public Vector<T> GetInverted()
        {
            dynamic xVal = X;
            dynamic yVal = Y;

            return new Vector<T>(-xVal, -yVal);
        }

        /// <summary>
        /// Returns a Vector that is a normal from the current Vector. Does not change the current Vector.
        /// </summary>
        /// <returns></returns>
        public Vector<T> GetNormalized()
        {
            if (Length == 0)
                return null;
            else
                return new Vector<T>(XValue / Length, YValue / Length);
        }

        public override string ToString()
        {
            return "{ " + X.ToString() + " : " + Y.ToString() + " }";
        }

        /// <summary>
        /// Converts the Vector object to a Vector2 object.
        /// </summary>
        /// <returns></returns>
        public Vector2 ToVector2()
        {
            return new Vector2((float)XValue, (float)YValue);
        }

        /// <summary>
        /// Returns the distance between the current vector and a given vector
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public double VectorDistance(Vector<T> other)
        {
            double result = Math.Sqrt(Math.Pow(XValue - other.XValue, 2) + Math.Pow(YValue - other.YValue, 2));

            return result;
        }
    }
}
